using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Perovich.GameObjects.Interfaces;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

#if XBOX
#else
using System.Windows;
using System.Windows.Navigation;
#endif

#if WINDOWS_PHONE
using Microsoft.Phone.Controls;

namespace Perovich.GameObjects.SilverlightWithXna
{
    
    /// <summary>
    /// Provides basic graphics device initialization, game logic, and rendering code for the game page of a Silverlight with Xna application.
    /// </summary>
    public class ObjectGamePage : PhoneApplicationPage, ICanHoldComponents, IDisposable
    {

        /// <summary>
        /// Gets the <see cref="IServiceProvider"/> for this <see cref="ObjectGamePage"/>.
        /// </summary>
        public IServiceProvider Services
        {
            get { return (Application.Current as IApp).Services; }
        }

        ComponentCollection _comps = new ComponentCollection();

        /// <summary>
        /// Gets the collection of components this <see cref="ObjectGamePage"/> manages.
        /// </summary>
        public ComponentCollection Components
        {
            get { return _comps; }
        }

        /// <summary>
        /// Triggered whenever the <see cref="SBatcheseses"/> property is changed.
        /// </summary>
        public event EventHandler<EventArgs> SpriteBatchSet;

        /// <summary>
        /// Gets or sets the <see cref="SpriteBatch"/> used to draw this <see cref="ObjectGamePage"/>.
        /// </summary>
        public SpriteBatchCollection SBatches
        {
            get { return _SBatches; }
            set
            {
                SpriteBatchCollection old = _SBatches;
                _SBatches = value;
                if (_SBatches != old)
                    SignalSpriteBatch();
            }
        }
        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="ObjectGamePage"/>'s components are signaled of changes to the <see cref="SBatcheseses"/> property.
        /// </summary>
        public bool SignalChildrenOfSBatchesChanges
        {
            get
            {
                return true;
            }
            set
            {
                value.Equals(true);
            }
        }

        /// <summary>
        /// Gets the <see cref="ContentManager"/> associated with this <see cref="ObjectGamePage"/>.
        /// </summary>
        public new ContentManager Content
        {
            get;
            private set;
        }


        /// <summary>
        /// Gets the <see cref="IGraphicsDeviceService"/> associated with this <see cref="ObjectGamePage"/>.
        /// </summary>
        public IGraphicsDeviceService SharedGraphicsManager
        {
            get
            {
                if (graphics == null)
                    SetGraphicsDevice();
                return graphics;
            }
            private set
            {
                graphics = value;
            }
        }

        /// <summary>
        /// Gets the <see cref="GraphicsDevice"/> associated with this <see cref="ObjectGamePage"/>.
        /// </summary>
        public GraphicsDevice GraphicsDevice
        {
            get
            {
                return SharedGraphicsManager.GraphicsDevice;
            }
        }


        private IGraphicsDeviceService graphics;

        /// <summary>
        /// Gets the <see cref="IGraphicsDeviceService"/> associated with this <see cref="ObjectGamePage"/>.
        /// </summary>
        protected IGraphicsDeviceService Graphics
        {
            get
            {
                if (graphics == null)
                    SetGraphicsDevice();
                return graphics;
            }
        }

        GameTimer timer;
        SpriteBatchCollection _SBatches;

        private void SignalSpriteBatch()
        {
            if ((SpriteBatchSet != null) && (SignalChildrenOfSBatchesChanges))
            {
                SpriteBatchSet(this, null);
            }
        }

        /// <summary>
        /// The <see cref="Color"/> of the game's background.
        /// </summary>
        public Color BackgroundColor
        {
            get;
            set;
        }

        public TimeSpan TargetElapsedTime
        {
            get
            {
                return timer.UpdateInterval;
            }
            set
            {
                timer.UpdateInterval = value;
            }
        }

        /// <summary>
        /// Constructs a new instance of the <see cref="ObjectGamePage"/> class.
        /// </summary>
        public ObjectGamePage()
            : base()
        {

            // Get the content manager from the application
            IApp a = Application.Current as IApp;
            Content = a.Content;


            // Create a timer for this page
            timer = new GameTimer();
            timer.UpdateInterval = TimeSpan.FromTicks(333333);
            timer.Update += OnUpdate;
            timer.Draw += OnDraw;
        }

        void SetGraphicsDevice()
        {
            // Look for the graphics device service in the game's service container
            SharedGraphicsManager = (Application.Current as IApp).Services.GetService(typeof(IGraphicsDeviceService)) as IGraphicsDeviceService;
        }

        /// <summary>
        /// Called after the <see cref="ObjectGamePage"/> and <see cref="GraphicsDevice"/> are created, but before <see cref="ObjectGamePage.LoadContent"/>. 
        /// </summary>
        protected virtual void Initialize()
        {
            Components.Initialize();
        }

        private void InitializeGraphics()
        {
            // Like our XNA pendant, we absolutely require the graphics device service
            if (SharedGraphicsManager == null)
                throw new InvalidOperationException("Graphics device service not found");


            Type.GetType("Microsoft.Xna.Framework.Graphics.GraphicsDeviceExtensions,Microsoft.Xna.Framework.Interop").GetMethod("SetSharingMode").Invoke(null, new object[] { SharedGraphicsManager.GraphicsDevice, true });

            Initialize();

            // Done, now we can register to the graphics device service's events
            subscribeToGraphicsDeviceService();

        }


        /// <summary>
        /// Called when the <see cref="ObjectGamePage"/> has determined that game logic needs to be processed. Override this method with game specific update code.
        /// </summary>
        /// <param name="gameTime">Time passed since the last call to <see cref="ObjectGamePage.Update"/>.</param>
        public virtual void Update(GameTime gameTime)
        {
            Components.Update(gameTime);
            Perovich.GameObjects.ComponentsExtensions.CheckColisions(this);
        }


        /// <summary>Called before the <see cref="Draw"/> function.</summary>
        /// <param name="gameTime">Provides a snapshot of the game's timing values.</param>
        protected virtual void DrawOnTargets(GameTime gameTime)
        {
            Components.DrawOnTargets(gameTime);
        }




        /// <summary>
        /// Called when the <see cref="ObjectGamePage"/> determines it is time to draw a frame. Override this method with game-specific rendering code. 
        /// </summary>
        /// <param name="gameTime">Time passed since the last call to <see cref="ObjectGamePage.Draw"/>.</param>
        public virtual void Draw(GameTime gameTime)
        {
            DrawOnTargets(gameTime);
            GraphicsDevice.Clear(BackgroundColor);
            Components.Draw(gameTime, GraphicsDevice, SBatches);

        }

        /// <summary>
        /// Immediately releases the unmanaged resources used by this object.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Allows the <see cref="ObjectGamePage"/> to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection.
        /// </summary>
        ~ObjectGamePage()
        {
            Dispose(false);
        }

        /// <summary>
        /// Releases all resources used by the <see cref="ObjectGamePage"/> class.
        /// </summary>
        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Unsubscribe from its events unset the graphics device service once.
                if (SharedGraphicsManager != null)
                {
                    unsubscribeFromGraphicsDeviceService();
                    SharedGraphicsManager = null;
                }

                timer.Dispose();

                Components.Dispose();
            }
        }



        /// <summary>
        ///   Called when graphics resources need to be loaded. Override this method to load
        ///   any game-specific graphics resources.
        /// </summary>
        protected virtual void LoadContent() { }

        /// <summary>
        ///   Called when graphics resources need to be unloaded. Override this method to unload
        ///   any game-specific graphics resources.
        /// </summary>
        protected virtual void UnloadContent() { }


        private void subscribeToGraphicsDeviceService()
        {

            // Register to the events of the graphics device service so we know when
            // the graphics device is set up, shut down or reset.
            SharedGraphicsManager.DeviceCreated += new EventHandler<EventArgs>(deviceCreated);
            SharedGraphicsManager.DeviceDisposing += new EventHandler<EventArgs>(deviceDisposing);

            // If a graphics device has already been created, we need to simulate the
            // DeviceCreated event that we did miss because we weren't born yet :)
            if (SharedGraphicsManager.GraphicsDevice != null)
            {
                LoadContent();
            }

        }


        private void unsubscribeFromGraphicsDeviceService()
        {

            // Unsubscribe from the events again
            SharedGraphicsManager.DeviceCreated -= new EventHandler<EventArgs>(deviceCreated);
            SharedGraphicsManager.DeviceDisposing -= new EventHandler<EventArgs>(deviceDisposing);

            // If the graphics device is still active, we give the component a chance
            // to clean up its data
            if (SharedGraphicsManager.GraphicsDevice != null)
            {
                UnloadContent();
            }

        }


        private void deviceCreated(object sender, EventArgs arguments)
        {
            LoadContent();
        }




        private void deviceDisposing(object sender, EventArgs arguments)
        {
            UnloadContent();
        }





        /// <summary>
        /// Calls <see cref="Initialize"/> and starts the game loop.
        /// </summary>
        /// <param name="e">not used</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            InitializeGraphics();

            // Set the sharing mode of the graphics device to turn on XNA rendering
            //SharedGraphicsManager.GraphicsDevice.SetSharingMode(true);
            
            // Create a new SpriteBatch, which can be used to draw textures.
            SBatches = new SpriteBatchCollection(SharedGraphicsManager.GraphicsDevice);

            // Start the timer
            timer.Start();

            base.OnNavigatedTo(e);
        }


        /// <summary>
        /// Stops the game loop.
        /// </summary>
        /// <param name="e">not used</param>
        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            // Stop the timer
            timer.Stop();

            Perovich.GameObjects.ComponentsExtensions.ResetHelpers();

            // Set the sharing mode of the graphics device to turn off XNA rendering
            //SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(false);
            Type.GetType("Microsoft.Xna.Framework.Graphics.GraphicsDeviceExtensions,Microsoft.Xna.Framework.Interop").GetMethod("SetSharingMode").Invoke(null, new object[] { SharedGraphicsManager.GraphicsDevice, false });


            base.OnNavigatedFrom(e);
        }

        private void OnUpdate(object sender, GameTimerEventArgs e)
        {
            GameTime time = new GameTime(e.TotalTime,e.ElapsedTime);
            Update(time);
        }

        private void OnDraw(object sender, GameTimerEventArgs e)
        {
            GameTime time = new GameTime(e.TotalTime, e.ElapsedTime);
            Draw(time);
        }

    }
}
#endif